﻿#include <QtQml>

#include "global/QmlNames.h"
#include "global/xnamespace.h"
#include "config/Config.h"
#include "model/RosterModel.h"
#include "model/GroupsModel.h"
#include "model/SessionsModel.h"
#include "model/MessagesManager.h"
#include "model/LocalUserModel.h"
#include "multimedia/AudioRecorder.h"
#include "multimedia/AudioPlayer.h"
#include "util/PhotoUtil.h"
#include "util/FileUtil.h"
#include "util/Screenshot.h"

#include "QmlObjectRegister.h"

X_BEGIN_NAMESPACE

/**
 * @class 关于在QML中注册C++类型。
 *
 * 注意区分可实例化与不可实例的QML类型区别。
 *
 * 参见：https://doc.qt.io/qt-5/qtqml-cppintegration-definetypes.html#registering-c-types-with-the-qml-type-system
 */

/**
 * @brief 注册枚举类型。
 *
 * 枚举类型是不可实例化QML类型。此功能基于Qt 5.8+版本的功能实现。
 */
void QmlObjectRegister::registerEnums(){
    qmlRegisterUncreatableMetaObject(X::staticMetaObject, QmlNames::ENUM_URI, V1, V1_0, "X", "Error: only used for enums.");

    // 比较奇怪的是，上面虽然注册了枚举值，但是却没有注册枚举类型。此处再重新注册枚举类型。
    qRegisterMetaType<X::AvailableStatus>();
    qRegisterMetaType<X::SessionType>();
    qRegisterMetaType<X::MessageContentType>();
}

/**
 * @brief 注册工具类，使用单例模式。
 *
 * 单例模式是不可实例化QML类型。
 */
void QmlObjectRegister::registerUtils()
{
    qmlRegisterSingletonType<AudioPlayer>(QmlNames::UTIL_URI, V1, V1_0, AudioPlayer::qmlName(), &AudioPlayer::qmlInstance);
    qmlRegisterSingletonType<AudioRecorder>(QmlNames::UTIL_URI, V1, V1_0, AudioRecorder::qmlName(), &AudioRecorder::qmlInstance);
    qmlRegisterSingletonType<Screenshot>(QmlNames::UTIL_URI, V1, V1_0, Screenshot::qmlName(), &Screenshot::qmlInstance);
    qmlRegisterSingletonType<PhotoUtil>(QmlNames::UTIL_URI, V1, V1_0, PhotoUtil::qmlName(), &PhotoUtil::qmlInstance);
    qmlRegisterSingletonType<FileUtil>(QmlNames::UTIL_URI, V1, V1_0, FileUtil::qmlName(), &FileUtil::qmlInstance);
}

/**
 * @brief 注册顶级模型，使用单例模式。
 *
 * 单例模式是不可实例化QML类型。
 */
void QmlObjectRegister::registerTopLevelModelSingleton()
{
    qmlRegisterSingletonType<Config>(QmlNames::MODEL_URI, V1, V1_0, Config::qmlName(), &Config::qmlInstance);
    qmlRegisterSingletonType<LocalUserModel>(QmlNames::MODEL_URI, V1, V1_0, LocalUserModel::qmlName(), &LocalUserModel::qmlInstance);
    qmlRegisterSingletonType<SessionsModel>(QmlNames::MODEL_URI, V1, V1_0, SessionsModel::qmlName(), &SessionsModel::qmlInstance);
    qmlRegisterSingletonType<RosterModel>(QmlNames::MODEL_URI, V1, V1_0, RosterModel::qmlName(), &RosterModel::qmlInstance);
    qmlRegisterSingletonType<GroupsModel>(QmlNames::MODEL_URI, V1, V1_0, GroupsModel::qmlName(), &GroupsModel::qmlInstance);
    qmlRegisterSingletonType<MessagesManager>(QmlNames::MODEL_URI, V1, V1_0, MessagesManager::qmlName(), &MessagesManager::qmlInstance);
}

/**
 * @brief 注册子模型类型，它需要提供给QML使用，QML使用它来创建对象。
 *
 * 因为子类型必须能够被访问到，包括创建对象，所以必须在QML中注册。
 * 参见：https://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html
 *
 * 另外，子类型集合不能使用QList返回，它缺少metaData。
 * 参见：https://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html
 *
 * In general, these are accessible from QML regardless of whether a QObject-derived class has been registered with the QML type system.
 * However, if a class is to be used in a way that requires the engine to access additional type information —
 * for example, if the class itself is to be used as a method parameter or property,
 * or if one of its enum types is to be used in this way — then the class may need to be registered.
 */
void QmlObjectRegister::registerSubModelTypes()
{
    qmlRegisterType<SessionModel>(QmlNames::MODEL_URI, V1, V1_0, SessionModel::qmlName());
    qmlRegisterType<ContactModel>(QmlNames::MODEL_URI, V1, V1_0, ContactModel::qmlName());
    qmlRegisterType<MessageModel>(QmlNames::MODEL_URI, V1, V1_0, MessageModel::qmlName());
    qmlRegisterType<MessageListModel>(QmlNames::MODEL_URI, V1, V1_0, MessageListModel::qmlName());
    qmlRegisterType<GroupModel>(QmlNames::MODEL_URI, V1, V1_0, GroupModel::qmlName());
    qmlRegisterType<GroupMemberListModel>(QmlNames::MODEL_URI, V1, V1_0, GroupMemberListModel::qmlName());
    qmlRegisterType<GroupMemberModel>(QmlNames::MODEL_URI, V1, V1_0, GroupMemberModel::qmlName());
}

X_END_NAMESPACE
